home *** CD-ROM | disk | FTP | other *** search
/ PC/CD Gamer UK 120 / CD Gamer Issue 120 (March 2003) (Disc 2).ISO / mods / Q2_Codered / codeRED1_0.exe / Data1.cab / cl_scrn.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-12-07  |  28.4 KB  |  1,412 lines

  1. /*
  2. Copyright (C) 1997-2001 Id Software, Inc.
  3.  
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License
  6. as published by the Free Software Foundation; either version 2
  7. of the License, or (at your option) any later version.
  8.  
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
  12.  
  13. See the GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  18.  
  19. */
  20. // cl_scrn.c -- master for refresh, status bar, console, chat, notify, etc
  21.  
  22. /*
  23.  
  24.   full screen console
  25.   put up loading plaque
  26.   blanked background with loading plaque
  27.   blanked background with menu
  28.   cinematics
  29.   full screen image for quit and victory
  30.  
  31.   end of unit intermissions
  32.  
  33.   */
  34.  
  35. #include "client.h"
  36.  
  37. float        scr_con_current;    // aproaches scr_conlines at scr_conspeed
  38. float        scr_conlines;        // 0.0 to 1.0 lines of console to display
  39.  
  40. qboolean    scr_initialized;        // ready to draw
  41.  
  42. int            scr_draw_loading;
  43.  
  44. vrect_t        scr_vrect;        // position of render window on screen
  45.  
  46.  
  47. cvar_t        *scr_viewsize;
  48. cvar_t        *scr_conspeed;
  49. cvar_t        *scr_centertime;
  50. cvar_t        *scr_showturtle;
  51. cvar_t        *scr_showpause;
  52. cvar_t        *scr_printspeed;
  53.  
  54. cvar_t        *scr_netgraph;
  55. cvar_t        *scr_timegraph;
  56. cvar_t        *scr_debuggraph;
  57. cvar_t        *scr_graphheight;
  58. cvar_t        *scr_graphscale;
  59. cvar_t        *scr_graphshift;
  60. cvar_t        *scr_drawall;
  61.  
  62. typedef struct
  63. {
  64.     int        x1, y1, x2, y2;
  65. } dirty_t;
  66.  
  67. dirty_t        scr_dirty, scr_old_dirty[2];
  68.  
  69. char        crosshair_pic[MAX_QPATH];
  70. int            crosshair_width, crosshair_height;
  71.  
  72. void SCR_TimeRefresh_f (void);
  73. void SCR_Loading_f (void);
  74.  
  75.  
  76. /*
  77. ===============================================================================
  78.  
  79. BAR GRAPHS
  80.  
  81. ===============================================================================
  82. */
  83.  
  84. /*
  85. ==============
  86. CL_AddNetgraph
  87.  
  88. A new packet was just parsed
  89. ==============
  90. */
  91. void CL_AddNetgraph (void)
  92. {
  93.     int        i;
  94.     int        in;
  95.     int        ping;
  96.  
  97.     // if using the debuggraph for something else, don't
  98.     // add the net lines
  99.     if (scr_debuggraph->value || scr_timegraph->value)
  100.         return;
  101.  
  102.     for (i=0 ; i<cls.netchan.dropped ; i++)
  103.         SCR_DebugGraph (30, 0x40);
  104.  
  105.     for (i=0 ; i<cl.surpressCount ; i++)
  106.         SCR_DebugGraph (30, 0xdf);
  107.  
  108.     // see what the latency was on this packet
  109.     in = cls.netchan.incoming_acknowledged & (CMD_BACKUP-1);
  110.     ping = cls.realtime - cl.cmd_time[in];
  111.     ping /= 30;
  112.     if (ping > 30)
  113.         ping = 30;
  114.     SCR_DebugGraph (ping, 0xd0);
  115. }
  116.  
  117.  
  118. typedef struct
  119. {
  120.     float    value;
  121.     int        color;
  122. } graphsamp_t;
  123.  
  124. static    int            current;
  125. static    graphsamp_t    values[1024];
  126.  
  127. /*
  128. ==============
  129. SCR_DebugGraph
  130. ==============
  131. */
  132. void SCR_DebugGraph (float value, int color)
  133. {
  134.     values[current&1023].value = value;
  135.     values[current&1023].color = color;
  136.     current++;
  137. }
  138.  
  139. /*
  140. ==============
  141. SCR_DrawDebugGraph
  142. ==============
  143. */
  144. void SCR_DrawDebugGraph (void)
  145. {
  146.     int        a, x, y, w, i, h;
  147.     float    v;
  148.     int        color;
  149.  
  150.     //
  151.     // draw the graph
  152.     //
  153.     w = scr_vrect.width;
  154.  
  155.     x = scr_vrect.x;
  156.     y = scr_vrect.y+scr_vrect.height;
  157.     Draw_Fill (x, y-scr_graphheight->value,
  158.         w, scr_graphheight->value, 8);
  159.  
  160.     for (a=0 ; a<w ; a++)
  161.     {
  162.         i = (current-1-a+1024) & 1023;
  163.         v = values[i].value;
  164.         color = values[i].color;
  165.         v = v*scr_graphscale->value + scr_graphshift->value;
  166.         
  167.         if (v < 0)
  168.             v += scr_graphheight->value * (1+(int)(-v/scr_graphheight->value));
  169.         h = (int)v % (int)scr_graphheight->value;
  170.         Draw_Fill (x+w-1-a, y - h, 1,    h, color);
  171.     }
  172. }
  173.  
  174. /*
  175. ===============================================================================
  176.  
  177. CENTER PRINTING
  178.  
  179. ===============================================================================
  180. */
  181.  
  182. char        scr_centerstring[1024];
  183. float        scr_centertime_start;    // for slow victory printing
  184. float        scr_centertime_off;
  185. int            scr_center_lines;
  186. int            scr_erase_center;
  187.  
  188. /*
  189. ==============
  190. SCR_CenterPrint
  191.  
  192. Called for important messages that should stay in the center of the screen
  193. for a few moments
  194. ==============
  195. */
  196. void SCR_CenterPrint (char *str)
  197. {
  198.     char    *s;
  199.     char    line[64];
  200.     int        i, j, l;
  201.  
  202.     strncpy (scr_centerstring, str, sizeof(scr_centerstring)-1);
  203.     scr_centertime_off = scr_centertime->value;
  204.     scr_centertime_start = cl.time;
  205.  
  206.     // count the number of lines for centering
  207.     scr_center_lines = 1;
  208.     s = str;
  209.     while (*s)
  210.     {
  211.         if (*s == '\n')
  212.             scr_center_lines++;
  213.         s++;
  214.     }
  215.  
  216.     // echo it to the console
  217.     Com_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n");
  218.  
  219.     s = str;
  220.     do    
  221.     {
  222.     // scan the width of the line
  223.         for (l=0 ; l<40 ; l++)
  224.             if (s[l] == '\n' || !s[l])
  225.                 break;
  226.         for (i=0 ; i<(40-l)/2 ; i++)
  227.             line[i] = ' ';
  228.  
  229.         for (j=0 ; j<l ; j++)
  230.         {
  231.             line[i++] = s[j];
  232.         }
  233.  
  234.         line[i] = '\n';
  235.         line[i+1] = 0;
  236.  
  237.         Com_Printf ("%s", line);
  238.  
  239.         while (*s && *s != '\n')
  240.             s++;
  241.  
  242.         if (!*s)
  243.             break;
  244.         s++;        // skip the \n
  245.     } while (1);
  246.     Com_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n");
  247.     Con_ClearNotify ();
  248. }
  249.  
  250.  
  251. void SCR_DrawCenterString (void)
  252. {
  253.     char    *start;
  254.     int        l;
  255.     int        j;
  256.     int        x, y;
  257.     int        remaining;
  258.  
  259. // the finale prints the characters one at a time
  260.     remaining = 9999;
  261.  
  262.     scr_erase_center = 0;
  263.     start = scr_centerstring;
  264.  
  265.     if (scr_center_lines <= 4)
  266.         y = viddef.height*0.35;
  267.     else
  268.         y = 48;
  269.  
  270.     do    
  271.     {
  272.     // scan the width of the line
  273.         for (l=0 ; l<40 ; l++)
  274.             if (start[l] == '\n' || !start[l])
  275.                 break;
  276.         x = (viddef.width - l*8)/2;
  277.         SCR_AddDirtyPoint (x, y);
  278.         for (j=0 ; j<l ; j++, x+=8)
  279.         {
  280.             Draw_Char (x, y, start[j]);    
  281.             if (!remaining--)
  282.                 return;
  283.         }
  284.         SCR_AddDirtyPoint (x, y+8);
  285.             
  286.         y += 8;
  287.  
  288.         while (*start && *start != '\n')
  289.             start++;
  290.  
  291.         if (!*start)
  292.             break;
  293.         start++;        // skip the \n
  294.     } while (1);
  295. }
  296.  
  297. void SCR_CheckDrawCenterString (void)
  298. {
  299.     scr_centertime_off -= cls.frametime;
  300.     
  301.     if (scr_centertime_off <= 0)
  302.         return;
  303.  
  304.     SCR_DrawCenterString ();
  305. }
  306.  
  307. //=============================================================================
  308.  
  309. /*
  310. =================
  311. SCR_CalcVrect
  312.  
  313. Sets scr_vrect, the coordinates of the rendered window
  314. =================
  315. */
  316. static void SCR_CalcVrect (void)
  317. {
  318.     int        size;
  319.  
  320.     // bound viewsize
  321.     if (scr_viewsize->value < 40)
  322.         Cvar_Set ("viewsize","40");
  323.     if (scr_viewsize->value > 100)
  324.         Cvar_Set ("viewsize","100");
  325.  
  326.     size = scr_viewsize->value;
  327.  
  328.     scr_vrect.width = viddef.width*size/100;
  329.     scr_vrect.width &= ~7;
  330.  
  331.     scr_vrect.height = viddef.height*size/100;
  332.     scr_vrect.height &= ~1;
  333.  
  334.     scr_vrect.x = (viddef.width - scr_vrect.width)/2;
  335.     scr_vrect.y = (viddef.height - scr_vrect.height)/2;
  336. }
  337.  
  338.  
  339. /*
  340. =================
  341. SCR_SizeUp_f
  342.  
  343. Keybinding command
  344. =================
  345. */
  346. void SCR_SizeUp_f (void)
  347. {
  348.     Cvar_SetValue ("viewsize",scr_viewsize->value+10);
  349. }
  350.  
  351.  
  352. /*
  353. =================
  354. SCR_SizeDown_f
  355.  
  356. Keybinding command
  357. =================
  358. */
  359. void SCR_SizeDown_f (void)
  360. {
  361.     Cvar_SetValue ("viewsize",scr_viewsize->value-10);
  362. }
  363.  
  364. /*
  365. =================
  366. SCR_Sky_f
  367.  
  368. Set a specific sky and rotation speed
  369. =================
  370. */
  371. void SCR_Sky_f (void)
  372. {
  373.     float    rotate;
  374.     vec3_t    axis;
  375.  
  376.     if (Cmd_Argc() < 2)
  377.     {
  378.         Com_Printf ("Usage: sky <basename> <rotate> <axis x y z>\n");
  379.         return;
  380.     }
  381.     if (Cmd_Argc() > 2)
  382.         rotate = atof(Cmd_Argv(2));
  383.     else
  384.         rotate = 0;
  385.     if (Cmd_Argc() == 6)
  386.     {
  387.         axis[0] = atof(Cmd_Argv(3));
  388.         axis[1] = atof(Cmd_Argv(4));
  389.         axis[2] = atof(Cmd_Argv(5));
  390.     }
  391.     else
  392.     {
  393.         axis[0] = 0;
  394.         axis[1] = 0;
  395.         axis[2] = 1;
  396.     }
  397.  
  398.     R_SetSky (Cmd_Argv(1), rotate, axis);
  399. }
  400.  
  401. //============================================================================
  402.  
  403. /*
  404. ==================
  405. SCR_Init
  406. ==================
  407. */
  408. void SCR_Init (void)
  409. {
  410.     scr_viewsize = Cvar_Get ("viewsize", "100", CVAR_ARCHIVE);
  411.     scr_conspeed = Cvar_Get ("scr_conspeed", "3", 0);
  412.     scr_showturtle = Cvar_Get ("scr_showturtle", "0", 0);
  413.     scr_showpause = Cvar_Get ("scr_showpause", "1", 0);
  414.     scr_centertime = Cvar_Get ("scr_centertime", "2.5", 0);
  415.     scr_printspeed = Cvar_Get ("scr_printspeed", "8", 0);
  416.     scr_netgraph = Cvar_Get ("netgraph", "0", 0);
  417.     scr_timegraph = Cvar_Get ("timegraph", "0", 0);
  418.     scr_debuggraph = Cvar_Get ("debuggraph", "0", 0);
  419.     scr_graphheight = Cvar_Get ("graphheight", "32", 0);
  420.     scr_graphscale = Cvar_Get ("graphscale", "1", 0);
  421.     scr_graphshift = Cvar_Get ("graphshift", "0", 0);
  422.     scr_drawall = Cvar_Get ("scr_drawall", "0", 0);
  423.  
  424. //
  425. // register our commands
  426. //
  427.     Cmd_AddCommand ("timerefresh",SCR_TimeRefresh_f);
  428.     Cmd_AddCommand ("loading",SCR_Loading_f);
  429.     Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
  430.     Cmd_AddCommand ("sizedown",SCR_SizeDown_f);
  431.     Cmd_AddCommand ("sky",SCR_Sky_f);
  432.  
  433.     scr_initialized = true;
  434. }
  435.  
  436.  
  437. /*
  438. ==============
  439. SCR_DrawNet
  440. ==============
  441. */
  442. void SCR_DrawNet (void)
  443. {
  444.     if (cls.netchan.outgoing_sequence - cls.netchan.incoming_acknowledged 
  445.         < CMD_BACKUP-1)
  446.         return;
  447.  
  448.     Draw_Pic (scr_vrect.x+64, scr_vrect.y, "net");
  449. }
  450.  
  451. /*
  452. ==============
  453. SCR_DrawPause
  454. ==============
  455. */
  456. void SCR_DrawPause (void)
  457. {
  458.     int        w, h;
  459.  
  460.     if (!scr_showpause->value)        // turn off for screenshots
  461.         return;
  462.  
  463.     if (!cl_paused->value)
  464.         return;
  465.  
  466.     Draw_GetPicSize (&w, &h, "pause");
  467.     Draw_Pic ((viddef.width-w)/2, viddef.height/2 + 8, "pause");
  468. }
  469.  
  470. /*
  471. ==============
  472. SCR_DrawLoading
  473. ==============
  474. */
  475. void SCR_DrawLoading (void)
  476. {
  477.     int        w, h;
  478.         
  479.     if (!scr_draw_loading)
  480.         return;
  481.  
  482.     scr_draw_loading = false;
  483.     Draw_GetPicSize (&w, &h, "loading");
  484.     Draw_Pic ((viddef.width-w)/2, (viddef.height-h)/2, "loading");
  485. }
  486.  
  487. //=============================================================================
  488.  
  489. /*
  490. ==================
  491. SCR_RunConsole
  492.  
  493. Scroll it up or down
  494. ==================
  495. */
  496. void SCR_RunConsole (void)
  497. {
  498. // decide on the height of the console
  499.     if (cls.key_dest == key_console)
  500.         scr_conlines = 0.5;        // half screen
  501.     else
  502.         scr_conlines = 0;                // none visible
  503.     
  504.     if (scr_conlines < scr_con_current)
  505.     {
  506.         scr_con_current -= scr_conspeed->value*cls.frametime;
  507.         if (scr_conlines > scr_con_current)
  508.             scr_con_current = scr_conlines;
  509.  
  510.     }
  511.     else if (scr_conlines > scr_con_current)
  512.     {
  513.         scr_con_current += scr_conspeed->value*cls.frametime;
  514.         if (scr_conlines < scr_con_current)
  515.             scr_con_current = scr_conlines;
  516.     }
  517.  
  518. }
  519.  
  520. /*
  521. ==================
  522. SCR_DrawConsole
  523. ==================
  524. */
  525. void SCR_DrawConsole (void)
  526. {
  527.     Con_CheckResize ();
  528.     
  529.     if (cls.state == ca_disconnected || cls.state == ca_connecting)
  530.     {    // forced full screen console
  531.         Con_DrawConsole (1.0);
  532.         return;
  533.     }
  534.  
  535.     if (cls.state != ca_active || !cl.refresh_prepped)
  536.     {    // connected, but can't render
  537.         Con_DrawConsole (0.5);
  538.         Draw_Fill (0, viddef.height/2, viddef.width, viddef.height/2, 0);
  539.         return;
  540.     }
  541.  
  542.     if (scr_con_current)
  543.     {
  544.         Con_DrawConsole (scr_con_current);
  545.     }
  546.     else
  547.     {
  548.         if (cls.key_dest == key_game || cls.key_dest == key_message)
  549.             Con_DrawNotify ();    // only draw notify in game
  550.     }
  551. }
  552.  
  553. //=============================================================================
  554.  
  555. /*
  556. ================
  557. SCR_BeginLoadingPlaque
  558. ================
  559. */
  560. void SCR_BeginLoadingPlaque (void)
  561. {
  562.     S_StopAllSounds ();
  563.     cl.sound_prepped = false;        // don't play ambients
  564.     CDAudio_Stop ();
  565.     if (cls.disable_screen)
  566.         return;
  567.     if (developer->value)
  568.         return;
  569.     if (cls.state == ca_disconnected)
  570.         return;    // if at console, don't bring up the plaque
  571.     if (cls.key_dest == key_console)
  572.         return;
  573.     if (cl.cinematictime > 0)
  574.         scr_draw_loading = 2;    // clear to balack first
  575.     else
  576.         scr_draw_loading = 1;
  577.     SCR_UpdateScreen ();
  578.     cls.disable_screen = Sys_Milliseconds ();
  579.     cls.disable_servercount = cl.servercount;
  580. }
  581.  
  582. /*
  583. ================
  584. SCR_EndLoadingPlaque
  585. ================
  586. */
  587. void SCR_EndLoadingPlaque (void)
  588. {
  589.     cls.disable_screen = 0;
  590.     Con_ClearNotify ();
  591. }
  592.  
  593. /*
  594. ================
  595. SCR_Loading_f
  596. ================
  597. */
  598. void SCR_Loading_f (void)
  599. {
  600.     SCR_BeginLoadingPlaque ();
  601. }
  602.  
  603. /*
  604. ================
  605. SCR_TimeRefresh_f
  606. ================
  607. */
  608. int entitycmpfnc( const entity_t *a, const entity_t *b )
  609. {
  610.     /*
  611.     ** all other models are sorted by model then skin
  612.     */
  613.     if ( a->model == b->model )
  614.     {
  615.         return ( ( int ) a->skin - ( int ) b->skin );
  616.     }
  617.     else
  618.     {
  619.         return ( ( int ) a->model - ( int ) b->model );
  620.     }
  621. }
  622.  
  623. void SCR_TimeRefresh_f (void)
  624. {
  625.     int        i;
  626.     int        start, stop;
  627.     float    time;
  628.  
  629.     if ( cls.state != ca_active )
  630.         return;
  631.  
  632.     start = Sys_Milliseconds ();
  633.  
  634.     if (Cmd_Argc() == 2)
  635.     {    // run without page flipping
  636.         R_BeginFrame( 0 );
  637.         for (i=0 ; i<128 ; i++)
  638.         {
  639.             cl.refdef.viewangles[1] = i/128.0*360.0;
  640.             R_RenderFrame (&cl.refdef);
  641.         }
  642.         R_EndFrame();
  643.     }
  644.     else
  645.     {
  646.         for (i=0 ; i<128 ; i++)
  647.         {
  648.             cl.refdef.viewangles[1] = i/128.0*360.0;
  649.  
  650.             R_BeginFrame( 0 );
  651.             R_RenderFrame (&cl.refdef);
  652.             R_EndFrame();
  653.         }
  654.     }
  655.  
  656.     stop = Sys_Milliseconds ();
  657.     time = (stop-start)/1000.0;
  658.     Com_Printf ("%f seconds (%f fps)\n", time, 128/time);
  659. }
  660.  
  661. /*
  662. =================
  663. SCR_AddDirtyPoint
  664. =================
  665. */
  666. void SCR_AddDirtyPoint (int x, int y)
  667. {
  668.     if (x < scr_dirty.x1)
  669.         scr_dirty.x1 = x;
  670.     if (x > scr_dirty.x2)
  671.         scr_dirty.x2 = x;
  672.     if (y < scr_dirty.y1)
  673.         scr_dirty.y1 = y;
  674.     if (y > scr_dirty.y2)
  675.         scr_dirty.y2 = y;
  676. }
  677.  
  678. void SCR_DirtyScreen (void)
  679. {
  680.     SCR_AddDirtyPoint (0, 0);
  681.     SCR_AddDirtyPoint (viddef.width-1, viddef.height-1);
  682. }
  683.  
  684. /*
  685. ==============
  686. SCR_TileClear
  687.  
  688. Clear any parts of the tiled background that were drawn on last frame
  689. ==============
  690. */
  691. void SCR_TileClear (void)
  692. {
  693.     int        i;
  694.     int        top, bottom, left, right;
  695.     dirty_t    clear;
  696.  
  697.     if (scr_drawall->value)
  698.         SCR_DirtyScreen ();    // for power vr or broken page flippers...
  699.  
  700.     if (scr_con_current == 1.0)
  701.         return;        // full screen console
  702.     if (scr_viewsize->value == 100)
  703.         return;        // full screen rendering
  704.     if (cl.cinematictime > 0)
  705.         return;        // full screen cinematic
  706.  
  707.     // erase rect will be the union of the past three frames
  708.     // so tripple buffering works properly
  709.     clear = scr_dirty;
  710.     for (i=0 ; i<2 ; i++)
  711.     {
  712.         if (scr_old_dirty[i].x1 < clear.x1)
  713.             clear.x1 = scr_old_dirty[i].x1;
  714.         if (scr_old_dirty[i].x2 > clear.x2)
  715.             clear.x2 = scr_old_dirty[i].x2;
  716.         if (scr_old_dirty[i].y1 < clear.y1)
  717.             clear.y1 = scr_old_dirty[i].y1;
  718.         if (scr_old_dirty[i].y2 > clear.y2)
  719.             clear.y2 = scr_old_dirty[i].y2;
  720.     }
  721.  
  722.     scr_old_dirty[1] = scr_old_dirty[0];
  723.     scr_old_dirty[0] = scr_dirty;
  724.  
  725.     scr_dirty.x1 = 9999;
  726.     scr_dirty.x2 = -9999;
  727.     scr_dirty.y1 = 9999;
  728.     scr_dirty.y2 = -9999;
  729.  
  730.     // don't bother with anything convered by the console)
  731.     top = scr_con_current*viddef.height;
  732.     if (top >= clear.y1)
  733.         clear.y1 = top;
  734.  
  735.     if (clear.y2 <= clear.y1)
  736.         return;        // nothing disturbed
  737.  
  738.     top = scr_vrect.y;
  739.     bottom = top + scr_vrect.height-1;
  740.     left = scr_vrect.x;
  741.     right = left + scr_vrect.width-1;
  742.  
  743.     if (clear.y1 < top)
  744.     {    // clear above view screen
  745.         i = clear.y2 < top-1 ? clear.y2 : top-1;
  746.         Draw_TileClear (clear.x1 , clear.y1,
  747.             clear.x2 - clear.x1 + 1, i - clear.y1+1, "backtile");
  748.         clear.y1 = top;
  749.     }
  750.     if (clear.y2 > bottom)
  751.     {    // clear below view screen
  752.         i = clear.y1 > bottom+1 ? clear.y1 : bottom+1;
  753.         Draw_TileClear (clear.x1, i,
  754.             clear.x2-clear.x1+1, clear.y2-i+1, "backtile");
  755.         clear.y2 = bottom;
  756.     }
  757.     if (clear.x1 < left)
  758.     {    // clear left of view screen
  759.         i = clear.x2 < left-1 ? clear.x2 : left-1;
  760.         Draw_TileClear (clear.x1, clear.y1,
  761.             i-clear.x1+1, clear.y2 - clear.y1 + 1, "backtile");
  762.         clear.x1 = left;
  763.     }
  764.     if (clear.x2 > right)
  765.     {    // clear left of view screen
  766.         i = clear.x1 > right+1 ? clear.x1 : right+1;
  767.         Draw_TileClear (i, clear.y1,
  768.             clear.x2-i+1, clear.y2 - clear.y1 + 1, "backtile");
  769.         clear.x2 = right;
  770.     }
  771.  
  772. }
  773.  
  774.  
  775. //===============================================================
  776.  
  777.  
  778. #define STAT_MINUS        10    // num frame for '-' stats digit
  779. char        *sb_nums[2][11] = 
  780. {
  781.     {"num_0", "num_1", "num_2", "num_3", "num_4", "num_5",
  782.     "num_6", "num_7", "num_8", "num_9", "num_minus"},
  783.     {"anum_0", "anum_1", "anum_2", "anum_3", "anum_4", "anum_5",
  784.     "anum_6", "anum_7", "anum_8", "anum_9", "anum_minus"}
  785. };
  786.  
  787. #define    ICON_WIDTH    24
  788. #define    ICON_HEIGHT    24
  789. #define    CHAR_WIDTH    16
  790. #define    ICON_SPACE    8
  791.  
  792.  
  793.  
  794. /*
  795. ================
  796. SizeHUDString
  797.  
  798. Allow embedded \n in the string
  799. ================
  800. */
  801. void SizeHUDString (char *string, int *w, int *h)
  802. {
  803.     int        lines, width, current;
  804.  
  805.     lines = 1;
  806.     width = 0;
  807.  
  808.     current = 0;
  809.     while (*string)
  810.     {
  811.         if (*string == '\n')
  812.         {
  813.             lines++;
  814.             current = 0;
  815.         }
  816.         else
  817.         {
  818.             current++;
  819.             if (current > width)
  820.                 width = current;
  821.         }
  822.         string++;
  823.     }
  824.  
  825.     *w = width * 8;
  826.     *h = lines * 8;
  827. }
  828.  
  829. void DrawHUDString (char *string, int x, int y, int centerwidth, int xor)
  830. {
  831.     int        margin;
  832.     char    line[1024];
  833.     int        width;
  834.     int        i;
  835.  
  836.     margin = x;
  837.  
  838.     while (*string)
  839.     {
  840.         // scan out one line of text from the string
  841.         width = 0;
  842.         while (*string && *string != '\n')
  843.             line[width++] = *string++;
  844.         line[width] = 0;
  845.  
  846.         if (centerwidth)
  847.             x = margin + (centerwidth - width*8)/2;
  848.         else
  849.             x = margin;
  850.         for (i=0 ; i<width ; i++)
  851.         {
  852.             Draw_Char (x, y, line[i]^xor);
  853.             x += 8;
  854.         }
  855.         if (*string)
  856.         {
  857.             string++;    // skip the \n
  858.             x = margin;
  859.             y += 8;
  860.         }
  861.     }
  862. }
  863.  
  864.  
  865. /*
  866. ==============
  867. SCR_DrawField
  868. ==============
  869. */
  870. void SCR_DrawField (int x, int y, int color, int width, int value)
  871. {
  872.     char    num[16], *ptr;
  873.     int        l;
  874.     int        frame;
  875.  
  876.     if (width < 1)
  877.         return;
  878.  
  879.     // draw number string
  880.     if (width > 5)
  881.         width = 5;
  882.  
  883.     SCR_AddDirtyPoint (x, y);
  884.     SCR_AddDirtyPoint (x+width*CHAR_WIDTH+2, y+23);
  885.  
  886.     Com_sprintf (num, sizeof(num), "%i", value);
  887.     l = strlen(num);
  888.     if (l > width)
  889.         l = width;
  890.     x += 2 + CHAR_WIDTH*(width - l);
  891.  
  892.     ptr = num;
  893.     while (*ptr && l)
  894.     {
  895.         if (*ptr == '-')
  896.             frame = STAT_MINUS;
  897.         else
  898.             frame = *ptr -'0';
  899.  
  900.         Draw_Pic (x,y,sb_nums[color][frame]);
  901.         x += CHAR_WIDTH;
  902.         ptr++;
  903.         l--;
  904.     }
  905. }
  906.  
  907.  
  908. /*
  909. ===============
  910. SCR_TouchPics
  911.  
  912. Allows rendering code to cache all needed sbar graphics
  913. ===============
  914. */
  915. void SCR_TouchPics (void)
  916. {
  917.     int        i, j;
  918.  
  919.     for (i=0 ; i<2 ; i++)
  920.         for (j=0 ; j<11 ; j++)
  921.             R_RegisterPic (sb_nums[i][j]);
  922.  
  923.     if (crosshair->value)
  924.     {
  925.         if (crosshair->value > 3 || crosshair->value < 0)
  926.             crosshair->value = 3;
  927.  
  928.         Com_sprintf (crosshair_pic, sizeof(crosshair_pic), "ch%i", (int)(crosshair->value));
  929.         Draw_GetPicSize (&crosshair_width, &crosshair_height, crosshair_pic);
  930.         if (!crosshair_width)
  931.             crosshair_pic[0] = 0;
  932.     }
  933. }
  934.  
  935. /*
  936. ================
  937. SCR_ExecuteLayoutString 
  938.  
  939. ================
  940. */
  941. void SCR_ExecuteLayoutString (char *s)
  942. {
  943.     int        x, y;
  944.     int        value;
  945.     char    *token;
  946.     int        width;
  947.     int        index;
  948.     clientinfo_t    *ci;
  949.  
  950.     if (cls.state != ca_active || !cl.refresh_prepped)
  951.         return;
  952.  
  953.     if (!s[0])
  954.         return;
  955.  
  956.     x = 0;
  957.     y = 0;
  958.     width = 3;
  959.  
  960.     while (s)
  961.     {
  962.         token = COM_Parse (&s);
  963.         if (!strcmp(token, "xl"))
  964.         {
  965.             token = COM_Parse (&s);
  966.             x = atoi(token);
  967.             continue;
  968.         }
  969.         if (!strcmp(token, "xr"))
  970.         {
  971.             token = COM_Parse (&s);
  972.             x = viddef.width + atoi(token);
  973.             continue;
  974.         }
  975.         if (!strcmp(token, "xv"))
  976.         {
  977.             token = COM_Parse (&s);
  978.             x = viddef.width/2 - 160 + atoi(token);
  979.             continue;
  980.         }
  981.  
  982.         if (!strcmp(token, "yt"))
  983.         {
  984.             token = COM_Parse (&s);
  985.             y = atoi(token);
  986.             continue;
  987.         }
  988.         if (!strcmp(token, "yb"))
  989.         {
  990.             token = COM_Parse (&s);
  991.             y = viddef.height + atoi(token);
  992.             continue;
  993.         }
  994.         if (!strcmp(token, "yv"))
  995.         {
  996.             token = COM_Parse (&s);
  997.             y = viddef.height/2 - 120 + atoi(token);
  998.             continue;
  999.         }
  1000.  
  1001.         if (!strcmp(token, "pic"))
  1002.         {    // draw a pic from a stat number
  1003.             token = COM_Parse (&s);
  1004.             value = cl.frame.playerstate.stats[atoi(token)];
  1005.             if (value >= MAX_IMAGES)
  1006.                 Com_Error (ERR_DROP, "Pic >= MAX_IMAGES");
  1007.             if (cl.configstrings[CS_IMAGES+value])
  1008.             {
  1009.                 SCR_AddDirtyPoint (x, y);
  1010.                 SCR_AddDirtyPoint (x+23, y+23);
  1011.                 Draw_Pic (x, y, cl.configstrings[CS_IMAGES+value]);
  1012.             }
  1013.             continue;
  1014.         }
  1015.  
  1016.         if (!strcmp(token, "client"))
  1017.         {    // draw a deathmatch client block
  1018.             int        score, ping, time;
  1019.  
  1020.             token = COM_Parse (&s);
  1021.             x = viddef.width/2 - 160 + atoi(token);
  1022.             token = COM_Parse (&s);
  1023.             y = viddef.height/2 - 120 + atoi(token);
  1024.             SCR_AddDirtyPoint (x, y);
  1025.             SCR_AddDirtyPoint (x+159, y+31);
  1026.  
  1027.             token = COM_Parse (&s);
  1028.             value = atoi(token);
  1029.             if (value >= MAX_CLIENTS || value < 0)
  1030.                 Com_Error (ERR_DROP, "client >= MAX_CLIENTS");
  1031.             ci = &cl.clientinfo[value];
  1032.  
  1033.             token = COM_Parse (&s);
  1034.             score = atoi(token);
  1035.  
  1036.             token = COM_Parse (&s);
  1037.             ping = atoi(token);
  1038.  
  1039.             token = COM_Parse (&s);
  1040.             time = atoi(token);
  1041.  
  1042.             DrawAltString (x+32, y, ci->name);
  1043.             DrawString (x+32, y+8,  "Score: ");
  1044.             DrawAltString (x+32+7*8, y+8,  va("%i", score));
  1045.             DrawString (x+32, y+16, va("Ping:  %i", ping));
  1046.             DrawString (x+32, y+24, va("Time:  %i", time));
  1047.  
  1048.             if (!ci->icon)
  1049.                 ci = &cl.baseclientinfo;
  1050.             Draw_Pic (x, y, ci->iconname);
  1051.             continue;
  1052.         }
  1053.  
  1054.         if (!strcmp(token, "ctf"))
  1055.         {    // draw a ctf client block
  1056.             int        score, ping;
  1057.             char    block[80];
  1058.  
  1059.             token = COM_Parse (&s);
  1060.             x = viddef.width/2 - 160 + atoi(token);
  1061.             token = COM_Parse (&s);
  1062.             y = viddef.height/2 - 120 + atoi(token);
  1063.             SCR_AddDirtyPoint (x, y);
  1064.             SCR_AddDirtyPoint (x+159, y+31);
  1065.  
  1066.             token = COM_Parse (&s);
  1067.             value = atoi(token);
  1068.             if (value >= MAX_CLIENTS || value < 0)
  1069.                 Com_Error (ERR_DROP, "client >= MAX_CLIENTS");
  1070.             ci = &cl.clientinfo[value];
  1071.  
  1072.             token = COM_Parse (&s);
  1073.             score = atoi(token);
  1074.  
  1075.             token = COM_Parse (&s);
  1076.             ping = atoi(token);
  1077.             if (ping > 999)
  1078.                 ping = 999;
  1079.  
  1080.             sprintf(block, "%3d %3d %-12.12s", score, ping, ci->name);
  1081.  
  1082.             if (value == cl.playernum)
  1083.                 DrawAltString (x, y, block);
  1084.             else
  1085.                 DrawString (x, y, block);
  1086.             continue;
  1087.         }
  1088.  
  1089.         if (!strcmp(token, "picn"))
  1090.         {    // draw a pic from a name
  1091.             token = COM_Parse (&s);
  1092.             SCR_AddDirtyPoint (x, y);
  1093.             SCR_AddDirtyPoint (x+23, y+23);
  1094.             Draw_Pic (x, y, token);
  1095.             continue;
  1096.         }
  1097.  
  1098.         if (!strcmp(token, "num"))
  1099.         {    // draw a number
  1100.             token = COM_Parse (&s);
  1101.             width = atoi(token);
  1102.             token = COM_Parse (&s);
  1103.             value = cl.frame.playerstate.stats[atoi(token)];
  1104.             SCR_DrawField (x, y, 0, width, value);
  1105.             continue;
  1106.         }
  1107.  
  1108.         if (!strcmp(token, "hnum"))
  1109.         {    // health number
  1110.             int        color;
  1111.             int        w, h;
  1112.  
  1113.             width = 3;
  1114.             value = cl.frame.playerstate.stats[STAT_HEALTH];
  1115.             if (value > 25)
  1116.                 color = 0;    // green
  1117.             else if (value > 0)
  1118.                 color = (cl.frame.serverframe>>2) & 1;        // flash
  1119.             else
  1120.                 color = 1;
  1121.             
  1122.             if (cl.frame.playerstate.stats[STAT_FLASHES] & 1)
  1123.             {
  1124.                 Draw_GetPicSize (&w, &h, "field_3");
  1125.                 Draw_Pic ((viddef.width-w)/2, (viddef.height-h)/2, "field_3");
  1126.             }
  1127.  
  1128.             SCR_DrawField (x, y, color, width, value);
  1129.             continue;
  1130.         }
  1131.  
  1132.         if (!strcmp(token, "anum"))
  1133.         {    // ammo number
  1134.             int        color;
  1135.             int        w, h;
  1136.  
  1137.             width = 3;
  1138.             value = cl.frame.playerstate.stats[STAT_AMMO];
  1139.             if (value > 5)
  1140.                 color = 0;    // green
  1141.             else if (value >= 0)
  1142.                 color = (cl.frame.serverframe>>2) & 1;        // flash
  1143.             else
  1144.                 continue;    // negative number = don't show
  1145.  
  1146.             if (cl.frame.playerstate.stats[STAT_FLASHES] & 4)
  1147.             {
  1148.                 Draw_GetPicSize (&w, &h, "field_3");
  1149.                 Draw_Pic ((viddef.width-w)/2, (viddef.height-h)/2, "field_3");
  1150.             }
  1151.  
  1152.             SCR_DrawField (x, y, color, width, value);
  1153.             continue;
  1154.         }
  1155.  
  1156.         if (!strcmp(token, "rnum"))
  1157.         {    // armor number
  1158.             int        color;
  1159.             int        w, h;
  1160.  
  1161.             width = 3;
  1162.             value = cl.frame.playerstate.stats[STAT_ARMOR];
  1163.             if (value < 1)
  1164.                 continue;
  1165.  
  1166.             color = 0;    // green
  1167.  
  1168.             if (cl.frame.playerstate.stats[STAT_FLASHES] & 2)
  1169.             {
  1170.                 Draw_GetPicSize (&w, &h, "field_3");
  1171.                 Draw_Pic ((viddef.width-w)/2, (viddef.height-h)/2, "field_3");
  1172.             }
  1173.  
  1174.             SCR_DrawField (x, y, color, width, value);
  1175.             continue;
  1176.         }
  1177.  
  1178.  
  1179.         if (!strcmp(token, "stat_string"))
  1180.         {
  1181.             token = COM_Parse (&s);
  1182.             index = atoi(token);
  1183.             if (index < 0 || index >= MAX_CONFIGSTRINGS)
  1184.                 Com_Error (ERR_DROP, "Bad stat_string index");
  1185.             index = cl.frame.playerstate.stats[index];
  1186.             if (index < 0 || index >= MAX_CONFIGSTRINGS)
  1187.                 Com_Error (ERR_DROP, "Bad stat_string index");
  1188.             DrawString (x, y, cl.configstrings[index]);
  1189.             continue;
  1190.         }
  1191.  
  1192.         if (!strcmp(token, "cstring"))
  1193.         {
  1194.             token = COM_Parse (&s);
  1195.             DrawHUDString (token, x, y, 320, 0);
  1196.             continue;
  1197.         }
  1198.  
  1199.         if (!strcmp(token, "string"))
  1200.         {
  1201.             token = COM_Parse (&s);
  1202.             DrawString (x, y, token);
  1203.             continue;
  1204.         }
  1205.  
  1206.         if (!strcmp(token, "cstring2"))
  1207.         {
  1208.             token = COM_Parse (&s);
  1209.             DrawHUDString (token, x, y, 320,0x80);
  1210.             continue;
  1211.         }
  1212.  
  1213.         if (!strcmp(token, "string2"))
  1214.         {
  1215.             token = COM_Parse (&s);
  1216.             DrawAltString (x, y, token);
  1217.             continue;
  1218.         }
  1219.  
  1220.         if (!strcmp(token, "if"))
  1221.         {    // draw a number
  1222.             token = COM_Parse (&s);
  1223.             value = cl.frame.playerstate.stats[atoi(token)];
  1224.             if (!value)
  1225.             {    // skip to endif
  1226.                 while (s && strcmp(token, "endif") )
  1227.                 {
  1228.                     token = COM_Parse (&s);
  1229.                 }
  1230.             }
  1231.  
  1232.             continue;
  1233.         }
  1234.     }
  1235. }
  1236.  
  1237.  
  1238. /*
  1239. ================
  1240. SCR_DrawStats
  1241.  
  1242. The status bar is a small layout program that
  1243. is based on the stats array
  1244. ================
  1245. */
  1246. void SCR_DrawStats (void)
  1247. {
  1248.     SCR_ExecuteLayoutString (cl.configstrings[CS_STATUSBAR]);
  1249. }
  1250.  
  1251.  
  1252. /*
  1253. ================
  1254. SCR_DrawLayout
  1255.  
  1256. ================
  1257. */
  1258. #define    STAT_LAYOUTS        13
  1259.  
  1260. void SCR_DrawLayout (void)
  1261. {
  1262.     if (!cl.frame.playerstate.stats[STAT_LAYOUTS])
  1263.         return;
  1264.     SCR_ExecuteLayoutString (cl.layout);
  1265. }
  1266.  
  1267. //=======================================================
  1268.  
  1269. /*
  1270. ==================
  1271. SCR_UpdateScreen
  1272.  
  1273. This is called every frame, and can also be called explicitly to flush
  1274. text to the screen.
  1275. ==================
  1276. */
  1277. void SCR_UpdateScreen (void)
  1278. {
  1279.     int numframes;
  1280.     int i;
  1281.     float separation[2] = { 0, 0 };
  1282.  
  1283.     // if the screen is disabled (loading plaque is up, or vid mode changing)
  1284.     // do nothing at all
  1285.     if (cls.disable_screen)
  1286.     {
  1287.         if (Sys_Milliseconds() - cls.disable_screen > 120000)
  1288.         {
  1289.             cls.disable_screen = 0;
  1290.             Com_Printf ("Loading plaque timed out.\n");
  1291.         }
  1292.         return;
  1293.     }
  1294.  
  1295.     if (!scr_initialized || !con.initialized)
  1296.         return;                // not initialized yet
  1297.  
  1298.     /*
  1299.     ** range check cl_camera_separation so we don't inadvertently fry someone's
  1300.     ** brain
  1301.     */
  1302.     if ( cl_stereo_separation->value > 1.0 )
  1303.         Cvar_SetValue( "cl_stereo_separation", 1.0 );
  1304.     else if ( cl_stereo_separation->value < 0 )
  1305.         Cvar_SetValue( "cl_stereo_separation", 0.0 );
  1306.  
  1307.     if ( cl_stereo->value )
  1308.     {
  1309.         numframes = 2;
  1310.         separation[0] = -cl_stereo_separation->value / 2;
  1311.         separation[1] =  cl_stereo_separation->value / 2;
  1312.     }        
  1313.     else
  1314.     {
  1315.         separation[0] = 0;
  1316.         separation[1] = 0;
  1317.         numframes = 1;
  1318.     }
  1319.  
  1320.     for ( i = 0; i < numframes; i++ )
  1321.     {
  1322.         R_BeginFrame( separation[i] );
  1323.  
  1324.         if (scr_draw_loading == 2)
  1325.         {    //  loading plaque over black screen
  1326.             int        w, h;
  1327.  
  1328.             R_SetPalette(NULL);
  1329.             scr_draw_loading = false;
  1330.             Draw_GetPicSize (&w, &h, "loading");
  1331.             Draw_Pic ((viddef.width-w)/2, (viddef.height-h)/2, "loading");
  1332. //            R_EndFrame();
  1333. //            return;
  1334.         } 
  1335.         // if a cinematic is supposed to be running, handle menus
  1336.         // and console specially
  1337.         else if (cl.cinematictime > 0)
  1338.         {
  1339.             if (cls.key_dest == key_menu)
  1340.             {
  1341.                 if (cl.cinematicpalette_active)
  1342.                 {
  1343.                     R_SetPalette(NULL);
  1344.                     cl.cinematicpalette_active = false;
  1345.                 }
  1346.                 M_Draw ();
  1347. //                R_EndFrame();
  1348. //                return;
  1349.             }
  1350.             else if (cls.key_dest == key_console)
  1351.             {
  1352.                 if (cl.cinematicpalette_active)
  1353.                 {
  1354.                     R_SetPalette(NULL);
  1355.                     cl.cinematicpalette_active = false;
  1356.                 }
  1357.                 SCR_DrawConsole ();
  1358. //                R_EndFrame();
  1359. //                return;
  1360.             }
  1361.             else
  1362.             {
  1363.                 SCR_DrawCinematic();
  1364. //                R_EndFrame();
  1365. //                return;
  1366.             }
  1367.         }
  1368.         else 
  1369.         {
  1370.  
  1371.             // make sure the game palette is active
  1372.             if (cl.cinematicpalette_active)
  1373.             {
  1374.                 R_SetPalette(NULL);
  1375.                 cl.cinematicpalette_active = false;
  1376.             }
  1377.  
  1378.             // do 3D refresh drawing, and then update the screen
  1379.             SCR_CalcVrect ();
  1380.  
  1381.             // clear any dirty part of the background
  1382.             SCR_TileClear ();
  1383.  
  1384.             V_RenderView ( separation[i] );
  1385.  
  1386.             SCR_DrawStats ();
  1387.             if (cl.frame.playerstate.stats[STAT_LAYOUTS] & 1)
  1388.                 SCR_DrawLayout ();
  1389.             if (cl.frame.playerstate.stats[STAT_LAYOUTS] & 2)
  1390.                 CL_DrawInventory ();
  1391.  
  1392.             SCR_DrawNet ();
  1393.             SCR_CheckDrawCenterString ();
  1394.  
  1395.             if (scr_timegraph->value)
  1396.                 SCR_DebugGraph (cls.frametime*300, 0);
  1397.  
  1398.             if (scr_debuggraph->value || scr_timegraph->value || scr_netgraph->value)
  1399.                 SCR_DrawDebugGraph ();
  1400.  
  1401.             SCR_DrawPause ();
  1402.  
  1403.             SCR_DrawConsole ();
  1404.  
  1405.             M_Draw ();
  1406.  
  1407.             SCR_DrawLoading ();
  1408.         }
  1409.     }
  1410.     R_EndFrame();
  1411. }
  1412.